Skip to content

A Brief Discussion on Synchronizing Entity Framework Navigation Properties and Foreign Keys

TLDR

  • The prerequisite for Navigation Property synchronization is that the related Entity must be in a Tracked state.
  • Any operation that triggers change tracking verification (such as Add, Entry, SaveChanges, or Find) will automatically synchronize navigation properties and foreign key properties.
  • Whether navigation properties are synchronized does not affect database correctness after SaveChanges is executed; EF Core automatically performs verification before saving.
  • Using main.Subs.Remove(sub) only removes the association; to delete child table data, you must use context.Subs.Remove(sub).
  • If the foreign key allows null, removing the association will set the foreign key to null instead of deleting the child record.

Entity Structure Definition

This test uses Microsoft.EntityFrameworkCore 8.

csharp
public partial class Main {
    public long Id { get; set; }
    public virtual ICollection<Sub> Subs { get; set; } = new List<Sub>();
}

public partial class Sub {
    public long Id { get; set; }
    public long MainId { get; set; }
    public virtual Main Main { get; set; }
}

Associating Child Tables Using Navigation Properties in the Main Table

The Impact of Tracking State on Synchronization

When you might encounter this issue: Before calling SaveChanges(), you need to confirm whether the navigation property has been automatically associated.

  • Untracked: If neither main nor sub are added to tracking, sub.Main is null.
  • Only Main Table Tracked: When main is added to tracking, sub will be tracked synchronously, and sub.Main will be updated automatically.
  • Only Child Table Tracked: If only sub is tracked and main is not, sub.Main will not be updated synchronously.
  • Tracked Before Setting: If you track main first and then execute main.Subs.Add(sub), sub.Main will be null before SaveChanges(), but it will synchronize automatically after execution.

Associating Main Tables Using Navigation Properties in the Child Table

When you might encounter this issue: When creating an association by directly manipulating the child table's navigation property.

  • Untracked: If you set sub.Main = main directly and neither is tracked, main.Subs remains an empty collection.
  • Only Main Table Tracked: When main is added to tracking but sub is not, main.Subs remains an empty collection.
  • Only Child Table Tracked: When only sub is tracked, EF will automatically track main synchronously; at this point, main.Subs will contain sub.

Setting Associations Using Foreign Key Properties

When you might encounter this issue: When creating an association by directly modifying the MainId field.

  • Only Child Table Tracked: If only sub is tracked and MainId is set, the navigation property will not synchronize.
  • Both Tracked: When both are tracked, the navigation property will synchronize automatically.
  • Modify Foreign Key After Tracking: If MainId is set after adding to tracking, the navigation property will not synchronize before SaveChanges(), but it will be updated after execution.
  • Retrieving Data with Find(): When using Find() to retrieve tracked Main data, the navigation property will synchronize automatically; if the Main retrieved is untracked, it will not synchronize.

Other Operations and Behavior Analysis

SaveChanges Failure and Entry Calls

  • SaveChanges Failure: Even if SaveChanges() fails due to database constraints (such as duplicate primary keys), the internal navigation properties of EF will still complete synchronization.
  • Entry Trigger: Executing context.Entry(entity) will force a change tracking verification, thereby synchronizing the navigation properties of tracked Entities.

Notes on Deleting Data

  • Deleting Child Table: You must use context.Subs.Remove(sub) to remove the data from the database.
  • Removing Association: Using main.Subs.Remove(sub) only removes the association.
    • For many-to-many associations, this operation removes the record from the join table.
    • If the foreign key allows null, this operation will set sub.MainId to null without deleting the child table data.

Change Log

  • 2024-08-12 Initial document creation.
  • 2026-05-29 Added link to the corresponding GitHub sample project.